#include "qemu/osdep.h"
#include "qemu/module.h"
#include "hw/i2c/i2c.h"
#include "migration/vmstate.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "qapi/visitor.h"
#include "trace.h"
#include "chardev/char-fe.h"
#include "qemu/error-report.h"
#include "hw/misc/utils.h"

#define MAX_REG 100
#define MAX_OFFSET 4 * 2
#define TYPE_PCA9555_I2CSLAVE "pca9555-i2c-slave"
#define PCA9555_I2CSLAVE(obj) OBJECT_CHECK(slave_state, (obj), TYPE_PCA9555_I2CSLAVE)
#define I2C_PATH "./output/data/mockdata/i2c/"

typedef struct
{
    I2CSlave parent_obj;
    CharBackend chrbe;
    uint8_t offset_width;
    uint8_t offset_bytes;
    char offset[MAX_OFFSET + 3];
    char *path;
    bool transfer_start;
    bool transfer_recv;
    uint8_t reg_id;
    cJSON *buffer;
    uint8_t tmp[MAX_REG];
    uint8_t tmp_size;
    bool tmp_save_flag; // 缓存是否需要修改的标志
    bool tmp_load_flag; // 缓存是否需要修改的标志
} slave_state;

static uint8_t hex_to_uint8(const char *hex_str)
{
    char *endptr;
    long int result;

    errno = 0;
    result = strtol(hex_str, &endptr, 0);
    if (errno != 0 || endptr == hex_str || *endptr != '\0' || result < 0 || result > UINT8_MAX)
    {
        fprintf(stderr, "Invalid number format or out of range");
        return 0;
    }

    return (uint8_t)result;
}

static void clear_tmp(slave_state *ss)
{
    memset(ss->tmp, 0xFF, MAX_REG);
    ss->tmp_size = 0;
}

static void load_buffer(slave_state *ss)
{
    clear_tmp(ss);
    cJSON *regs = cJSON_GetObjectItemCaseSensitive(ss->buffer, ss->offset);
    int reg_length = cJSON_GetArraySize(regs);
    for (int i = 0; i < reg_length; i++)
    {
        cJSON *item = cJSON_GetArrayItem(regs, i);
        if (item)
        {
            ss->tmp[ss->tmp_size] = hex_to_uint8(item->valuestring);
            ss->tmp_size++;
        }
        else
        {
            warn_report("Invaild arryay index: %d\n", i);
        }
    }
    ss->tmp_size = 0;
}

static void save_buffer(slave_state *ss)
{
    cJSON *regs = cJSON_GetObjectItemCaseSensitive(ss->buffer, ss->offset);
    // int reg_length = cJSON_GetArraySize(regs);
    char mid[5] = "0x";
    for (int i = 0; i < ss->tmp_size; i++)
    {
        cJSON *item = cJSON_GetArrayItem(regs, i);
        if (item)
        {
            sprintf(mid + 2, "%02X", ss->tmp[i]);
            cJSON_SetValuestring(item, mid);
        }
        else
        {
            warn_report("Invaild arryay index: %d\n", i);
        }
    }
}

static int pca9555_i2cslave_event(I2CSlave *i2c, enum i2c_event event)
{
    slave_state *s = PCA9555_I2CSLAVE(i2c);

    switch (event)
    {
        /* 数据传输开始，打开文件 */
    case I2C_START_SEND:
        s->tmp_save_flag = true;
        s->tmp_load_flag = true;
        s->transfer_start = true;
        s->transfer_recv = false;
        s->offset_bytes = 0;
        strcpy(s->offset, "0x");
        clear_tmp(s);
        break;
    case I2C_START_RECV:
        s->tmp_save_flag = false;
        s->tmp_load_flag = true;
        s->transfer_start = true;
        s->transfer_recv = true;
        break;
        /* 数据传输结束，关闭文件 */
    case I2C_FINISH:
        s->transfer_start = false;
        if (s->tmp_save_flag)
        {
            save_buffer(s);
        }
        write_json(s->path, s->buffer);
        break;
    case I2C_NACK:
        break;
    }
    return 0;
}

static uint8_t pca9555_i2cslave_rx(I2CSlave *i2c)
{
    slave_state *s = PCA9555_I2CSLAVE(i2c);
    if (s->tmp_load_flag)
    {
        load_buffer(s);
        s->tmp_load_flag = false;
    }
    int rx = s->tmp[s->tmp_size];
    s->tmp_size++;
    return rx;
}

static int pca9555_i2cslave_tx(I2CSlave *i2c, uint8_t data)
{
    /* 前两个参数计算偏移量，后面的参数为要写入的数据 */
    slave_state *s = PCA9555_I2CSLAVE(i2c);
    if (!s->transfer_recv)
    {
        sprintf(s->offset + (s->offset_bytes + 1) * 2, "%02X", data);
        s->offset_bytes++;
        if (s->offset_bytes == s->offset_width)
        {
            s->transfer_recv = true;
        }
    }
    else
    {
        s->tmp[s->tmp_size] = data;
        s->tmp_size++;
    }
    return 0;
}

static void pca9555_i2cslave_reset(DeviceState *dev)
{
    slave_state *s = PCA9555_I2CSLAVE(dev);
    s->transfer_start = false;
}

static void pca9555_i2cslave_realize(DeviceState *dev, Error **errp)
{
    slave_state *s = PCA9555_I2CSLAVE(dev);
    s->transfer_recv = false;
    s->offset_width = 1;
    s->tmp_size = 0;
    s->path = g_strdup_printf("%s%d/0x42_pca9555.json", I2C_PATH, s->reg_id);
    s->buffer = read_json(s->path);
}

static void generic_i2cslave_access_addr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp)
{
    uint8_t *ptr = (uint8_t *)object_field_prop_ptr(obj, (Property *)opaque);
    visit_type_uint8(v, name, ptr, errp);
}

static const PropertyInfo generic_i2cslave_prop_regaddr = {
    .name = "uint8",
    .get = generic_i2cslave_access_addr,
    .set = generic_i2cslave_access_addr,
    // .realized_set_allowed = true,
};

static Property pca9555_i2cslave_properties[] = {
    DEFINE_PROP_UNSIGNED_NODEFAULT("reg-id", slave_state, reg_id, generic_i2cslave_prop_regaddr, uint8_t),
    DEFINE_PROP_END_OF_LIST(),
};

static void pca9555_i2cslave_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
    k->event = pca9555_i2cslave_event;
    k->recv = pca9555_i2cslave_rx;
    k->send = pca9555_i2cslave_tx;
    dc->reset = pca9555_i2cslave_reset;
    dc->realize = pca9555_i2cslave_realize;
    device_class_set_props(dc, pca9555_i2cslave_properties);
}

static void pca9555_i2cslave_init(Object *obj)
{
    slave_state *s = PCA9555_I2CSLAVE(obj);
    s->transfer_start = false;
}

static const TypeInfo pca9555_i2cslave_info = {
    .name = TYPE_PCA9555_I2CSLAVE,
    .parent = TYPE_I2C_SLAVE,
    .instance_size = sizeof(slave_state),
    .class_init = pca9555_i2cslave_class_init,
    .instance_init = pca9555_i2cslave_init,
};

static void pca9555_i2cslave_register_types(void)
{
    type_register_static(&pca9555_i2cslave_info);
}

type_init(pca9555_i2cslave_register_types)